import { AUTHORITY_KEY } from '@/common/decorators/authority.decorator';
import { AuthService } from '@/modules/auth/auth.service';
import { CanActivate, ExecutionContext, HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Injectable()
export class AuthorityGuard implements CanActivate {
  constructor(
    private readonly reflector: Reflector,
    private readonly authService: AuthService
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    /**
     * 权限身份守卫，[] 中为建议携带的特殊字符以供阅读
     * 1. role [*] 角色标识 用于粗颗粒度授权
     * 2. permission [:] 权限标识 用于细颗粒度授权
     * 3. open [无] 开放
     * 4. justlogin [无] 仅需登录
     */

    const authority = this.reflector.get<string[]>(AUTHORITY_KEY, context.getHandler());

    // 空缺 直接放行
    if (!authority || !authority?.length) return true;

    // 提取出角色和权限（有*的为角色 如admin*，否则为权限 如test:create）
    const needRoles = authority.filter((item) => item && item.includes('*')).map((i) => i.replace(/\*/g, '')); // 去除角色字段的*号
    const needPermissions = authority.filter((item) => item && !item.includes('*'));

    /**
     * 特殊的权限标识：open 直接放行
     */
    if (needPermissions.includes('open')) return true;

    // token提取
    const request = context.switchToHttp().getRequest();
    const token = request.headers.authorization?.replace('Bearer', '').trim(); // Bearer Token
    if (!token) {
      throw new HttpException('请登录!', HttpStatus.UNAUTHORIZED);
    }

    // token验证
    const verifyRes = await this.authService.verifyToken(token);
    if (!verifyRes.success) {
      throw new HttpException(verifyRes.errMsg, HttpStatus.UNAUTHORIZED);
    }

    // token解析
    const { data: tokenInfo } = this.authService.parseToken(token);
    if (!tokenInfo?.access) {
      throw new HttpException('Token解析失败!', HttpStatus.UNAUTHORIZED);
    }

    /**
     * 特殊的权限标识：justlogin 仅需登录
     * 在已登录状态下无须其他权限，直接放行
     * 但是会进行 token 验证是否有效登录，所以相关 token 接口如 refreshToken 等禁止添加该权限标识，否则会导致静默刷新 token 时陷入死循环
     */
    const isJustLogin = needPermissions.includes('justlogin');
    if (isJustLogin) return true;

    // 获取身份信息
    let { data: identityData } = await this.authService.parseIdentity(tokenInfo);

    // 超级管理员直接放行
    const myRoles = identityData.roles;
    if (myRoles?.includes('admin')) return true;

    // 角色校验
    if (needRoles.length > 0) {
      // 必须满足全部角色授权
      const allrole = needRoles.every((role) => myRoles?.includes(role));
      if (!allrole) {
        throw new HttpException('未授权!', HttpStatus.FORBIDDEN);
      }
    }

    // 权限校验
    const myPermissions = identityData.permissions;
    if (needPermissions.length > 0) {
      // 必须满足全部权限授权
      const allpermission = needPermissions.every((permission) => myPermissions?.includes(permission));
      if (!allpermission) {
        throw new HttpException('未授权!', HttpStatus.FORBIDDEN);
      }
    }

    return true;
  }
}
